/**
 * @file Define the BaseService class in this module
 * 
 * @author Steve Gao<gaofu@parllay.com>
 */
const async         = require('async');
const domain        = require('domain');
const dateformat    = require('dateformat');
const EventEmitter  = require('events');

const redisKey              = require('../redisKey');
const ApplicationContext    = require('../application-context').ApplicationContext;
const {Configuration, SystemServices, ApplicationEvents} = require('../constants');

/**
 * 服务基类，实现所有服务需要的基础服务
 * 
 * @class BaseService
 * @constructor
 * @param {OnWallContext} context
 * 
 * @extends EventEmitter
 */
class BaseService extends EventEmitter {
    
    get logger() {
        if (!this._logger) {
            let logService = this.getSystemService(SystemServices.LOG_SERVICE);
            this._logger = logService ? logService.getLogger() : console;
        }

        return this._logger;
    }

    constructor(context) {
        super();
        this.setContext(context);
    }

    get Context() {
        return this._context;
    }

    get qiniuConfig() {
        return this.getApplicationConfiguration(Configuration.QINIU);
    }

    /**
     * 设置服务执行的上下文环境
     * 
     * @name setContext
     * @param {OnWallContext} context - 应用执行上下文
     * 
     */
    setContext(context) {
        this._context = context;
    }

    get ContextClass() {
        if (!this._contextClass) {
            this._contextClass = require('../application-context').ApplicationContext;
        }

        return this._contextClass;
    }

    /**
     * 格式化日期为MySQL支持的格式
     * 
     * @name formatDate
     * @param {Date} date - 要格式化的日期对象
     * 
     * @returns {String}
     */
    formatDate(date) {
        return dateformat(date, 'yyyy-mm-dd hh:MM:ss');
    }

    /**
     * 根据配置获取相应的连接客户端
     * 
     * @name acquireStorageClients
     * @param {Object} config - 配置
     * @param {Function} successCallback - 成功回调函数
     * @param {Function} failCallback - 失败回调函数
     */
    acquireStorageClients(config, successCallback, failCallback) {
        var me = this;
        var options = {};
        if(config.mysql) {
            options.mysql = function(callback) {
                me.Context.acquireMySQLClient(callback);
            };
        }
        if(config.redis) {
            options.redis = function(callback) {
                me.Context.acquireRedisClient(callback);
            };
        }

        async.parallel(options, function(error, result) {
            if(error) {
                //TODO: add log here
                var releaseConfig = {};
                if(result && result.redis) {
                    releaseConfig.redis = result.redis;
                }
                if(result && result.mysql) {
                    releaseConfig.mysql = result.mysql;
                }
                me.releaseStorageClients(releaseConfig);
                failCallback(error.message);
            } else {
                // Add domain context check here for fatal error
                if (domain.active) {
                    domain.active.on('cleanup', function() {
                        // Added code to clean data
                        /*if (options.redis && result.redis) {
                            me.Context.releaseRedisClient(result.redis);
                        }

                        if (options.redis && result.mysql) {
                            me.Context.releaseMySQLClient(result.mysql);
                        }*/
                        console.log('cleanup');
                    });
                }
                successCallback(result);
            }
        });
    }

    /**
     * 根据释放指定的连接客户端
     * 
     * @name releaseStorageClients
     * @param {Object} config - 配置
     */
    releaseStorageClients(clients) {
        if(clients.mysql) {
            this.Context.releaseMySQLClient(clients.mysql);
        }

        if(clients.redis) {
            this.Context.releaseRedisClient(clients.redis);
        }
    }

    /**
     * Get the system configuration information in database 
     * 
     * @name getSystemConfiguration
     * @param {String} key - 指定配置项
     * @param {Any} defaultValue - 缺省值
     * @param {Function} parentCallback - 回调函数
     * 
     * @returns {void}
     */
    getSystemConfiguration(key, defaultValue, parentCallback) {
        let me = this;
        this.acquireStorageClients({
            redis: true,
            mysql: true
        }, function(clients) {
            var redis = clients.redis;
            var mysql = clients.mysql;
            var configKey = redisKey.system_configurations();
            async.waterfall([
                function(callback) {
                    redis.hgetall(configKey, callback);
                }, function(config, callback) {
                    if(config && config[key]) {
                        me.releaseStorageClients(clients);
                        parentCallback(config[key]);
                    } else {
                        callback(null);
                    }
                }, function(callback) {
                    mysql.query('SELECT * FROM system_configurations', function(error, result) {
                        if(error) {
                            me.releaseStorageClients(clients);
                            parentCallback(defaultValue);
                        } else {
                            callback(null, result);
                        }
                    });
                }, function(result, callback) {
                    if (result && result.length > 0) {
                        var multi = redis.multi();
                        result.forEach(function(config) {
                            if(config.identifier === key) {
                                defaultValue = config.value;
                            }
                            multi.hset(configKey, config.identifier, config.value);
                        });
                        multi.exec(callback);
                    } else {
                        me.releaseStorageClients(clients);
                        parentCallback(defaultValue);
                    }
                }, function(result) {
                    me.releaseStorageClients(clients);
                    parentCallback(defaultValue);
                }
            ]);
        }, function(error) {
            parentCallback(defaultValue);
        });
    }

    /**
     * Get registered system service
     * 
     * @name getSystemService
     * @param {String} name - The name of registered service
     * 
     * @returns {Service}
     */
    getSystemService(name) {
        if(this.Context && this.Context instanceof this.ContextClass) {
            return this.Context.getSystemService(name);
        }
        
        return null;
    }

    /**
     * Get the application configuration information
     * 
     * @name getApplicationConfiguration
     * @param {String} name - The configuration name
     * 
     * @returns {Object}
     */
    getApplicationConfiguration(name) {
        
        if(this.Context) {
            return this.Context.getConfiguration(name);
        }

        return null;
    }

    reportError(error) {
        this.Context.reportError(ApplicationEvents.SERVICE_ERROR, error);
    }

    /**
     * Get image url with CDN
     * 
     * @name getCDNImageURL
     * @param {String} image - The original image url
     */
    getCDNImageURL(image) {
        if(image.startsWith(this.qiniuConfig.host) || image.startsWith('http://') || image.startsWith('https://') || image.startsWith('//')) {
            return image;
        } else {
            return `${this.qiniuConfig.host}${image}`;
        }
    }
}

exports.BaseService = BaseService;